package com.cosmoplat.xikai.business.scada;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.cosmoplat.xikai.business.domain.DeviceConfig;
import com.cosmoplat.xikai.business.domain.DeviceLog;
import com.cosmoplat.xikai.business.domain.DeviceThingModel;
import com.cosmoplat.xikai.business.enums.DeviceParamsEnum;
import com.cosmoplat.xikai.business.enums.MultiaddressEnum;
import com.cosmoplat.xikai.business.service.BaseDeliveryInfoService;
import com.cosmoplat.xikai.business.service.DeviceConfigService;
import com.cosmoplat.xikai.business.service.DeviceThingModelService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;
import java.util.stream.Collectors;

@Component
@Slf4j
public class ScadaData {
    //从站地址位数据
    private static final HashMap<String, DeviceLog> SCADA_DATA_MAP = new HashMap<String, DeviceLog>();
    //发送命令map
    private static final HashMap<String, String> SEND_MAP = new HashMap<String, String>();
    //保存多字节数据
    private static final HashMap<String, DeviceLog> MULTI_REGISTER_MAP = new HashMap<String, DeviceLog>();
    //读取设备配置的地址位
    private static final HashMap<String, List<DeviceThingModel>> DEVICE_THING_MODEL_MAP = new HashMap<>();
    @Autowired
    private DeviceThingModelService deviceThingModelService;
    @Autowired
    private DeviceConfigService deviceConfigService;
    @Autowired
    private static BaseDeliveryInfoService baseDeliveryInfoService;
    private static String deviceCode;

    // plc 获取数据的有效时间
    private static Integer plcValidateTime;

    @Value("${plc.validateTime:3}")
    private void setPlcValidateTime(Integer plcValidateTime) {
        this.plcValidateTime = plcValidateTime;
    }

    @Value("${device.code}")
    public void setDeviceCode(String deviceCode) {
        this.deviceCode = deviceCode;
    }

    @Autowired
    public void setBaseDeliveryInfoService(BaseDeliveryInfoService baseDeliveryInfoService) {
        this.baseDeliveryInfoService = baseDeliveryInfoService;
    }


    /**
     * 获取保存到实时数据
     *
     * @param identifier
     * @return
     */
    public static DeviceLog getDeviceLog(String identifier) {
        if (SCADA_DATA_MAP.containsKey(identifier)) {
            DeviceLog deviceLog = SCADA_DATA_MAP.get(identifier);
            //获取数据保存时间
            Date dataTime = deviceLog.getDataTime();
            //获取系统当前时间
            Date date = new Date();
            //根据系统进行对比x秒,判断是否在范围内,如果不在范围内则认为是断开链接的数据
//            return deviceLog;
            //TODO 放开注释
            long maxTime = date.getTime() + 1000 * plcValidateTime;
            long minTime = date.getTime() - 1000 * plcValidateTime;
//            返回数据
            if (maxTime > dataTime.getTime() && minTime < dataTime.getTime()) {
                return deviceLog;
            } else return null;
        } else return null;
    }

    /**
     * 保存到实时数据的map接口
     *
     * @param identifier
     * @param deviceLog
     */
    public static void addDeviceLog(String identifier, DeviceLog deviceLog) {
        if (judgmentIdentifier(identifier)) {
            deviceLog.setVal(new BigDecimal(deviceLog.getVal()).divide(new BigDecimal(1000),2,BigDecimal.ROUND_HALF_UP).toPlainString());
        }
        SCADA_DATA_MAP.put(identifier, deviceLog);
//        log.info(JSON.toJSONString(SCADA_DATA_MAP));
    }
    public static boolean judgmentIdentifier(String identifier) {
        String currentMaximumMomentName = DeviceParamsEnum.CURRENT_MAXIMUM_MOMENT.getName();
        String maximumWorkingMonment1 = DeviceParamsEnum.MAXIMUM_WORKING_MONMENT1.getName();
        String maximumWorkingMonment2 = DeviceParamsEnum.MAXIMUM_WORKING_MONMENT2.getName();
        String setMaximumWorkingMonment1 = DeviceParamsEnum.SET_MAXIMUM_WORKING_MONMENT1.getName();
        String setMaximumWorkingMonment2 = DeviceParamsEnum.SET_MAXIMUM_WORKING_MONMENT2.getName();

        if (identifier.equals(maximumWorkingMonment1)||identifier.equals(maximumWorkingMonment2)
                ||identifier.equals(setMaximumWorkingMonment1)||identifier.equals(currentMaximumMomentName)||identifier.equals(setMaximumWorkingMonment2)) {
            return true;
        }else {
            return false;
        }
    }
    /**
     * 保存到发送的接口中
     *
     * @param identifier
     * @param result
     */
    public static void putSendMap(String identifier, String result) {
        SEND_MAP.put(identifier, result);
    }

    /**
     * 清除发送map
     *
     * @param identifier
     */
    public static void removeSendMap(String identifier) {
        SEND_MAP.remove(identifier);
    }

    /**
     * 获取发送map返回的值
     *
     * @param identifier
     * @return
     */
    public static String getSendMap(String identifier) {
        return SEND_MAP.containsKey(identifier) ? SEND_MAP.get(identifier) : null;
    }

    /**
     * 启动时加载物模型
     */
    @Bean
    public void putDeviceThingModel() {
        //获取设备的id
        DeviceConfig deviceConfig = deviceConfigService.getOne(new LambdaQueryWrapper<DeviceConfig>().eq(DeviceConfig::getCode, deviceCode));
        List<DeviceThingModel> list = deviceThingModelService.list(new LambdaQueryWrapper<DeviceThingModel>());
        //提取是否是多地址位
        List<DeviceThingModel> thingModelList = list.stream().filter(s -> s.getNumberOfPoints() == 4).collect(Collectors.toList());
        /**
         通常，单地址位使用一个地址字段表示一个参数，而多地址位需由两个连续地址字段共同表示。由于数据库仅支持单个地址字段存储，因此将多地址位中的首个地址字段存入数据库，后续地址字段则由应用程序通过Map结构进行维护。
         */
        thingModelList.forEach(thingModel -> {
            DeviceThingModel deviceThingModel = new DeviceThingModel();
            BeanUtils.copyProperties(thingModel, deviceThingModel);
            deviceThingModel.setPoint(String.valueOf(Integer.parseInt(thingModel.getPoint()) + 1));
            deviceThingModel.setIdentifier(thingModel.getIdentifier() + MultiaddressEnum.MULTIADDRESS.getName());
            list.add(deviceThingModel);
        });
        DEVICE_THING_MODEL_MAP.put(deviceCode, list);
    }

    /**
     * 获取物模型参数
     *
     * @param identifier
     * @param index
     * @return
     */
    public static DeviceThingModel getDeviceThingModel(String identifier, Integer index) {
        return getDeviceThingModel(deviceCode, identifier, index);
    }

    /**
     * 获取物模型标识
     * <p>
     * index 1:identifier 2: point(实际的数值点位) 3:id(device_thing_model表id) 4:materialCode
     */
    public static DeviceThingModel getDeviceThingModel(String deviceCode, String identifier, Integer index) {
        switch (index) {
            case 1:
                for (DeviceThingModel deviceThingModel : DEVICE_THING_MODEL_MAP.get(deviceCode)) {
                    if (identifier.equals(deviceThingModel.getIdentifier())) {
                        return deviceThingModel;
                    }
                }
                break;
            case 2:
                for (DeviceThingModel deviceThingModel : DEVICE_THING_MODEL_MAP.get(deviceCode)) {
                    if (identifier.equals(deviceThingModel.getPoint())) {
                        return deviceThingModel;
                    }
                }
                break;
            case 3:
                for (DeviceThingModel deviceThingModel : DEVICE_THING_MODEL_MAP.get(deviceCode)) {
                    if (deviceThingModel.getId()==Integer.parseInt(identifier)) {
                        return deviceThingModel;
                    }
                }
                break;
            case 4:
                for (DeviceThingModel deviceThingModel : DEVICE_THING_MODEL_MAP.get(deviceCode)) {
                    if (identifier.equals(deviceThingModel.getMaterialCode())) {
                        return deviceThingModel;
                    }
                }
                break;

        }

        return null;
    }

    /**
     * 通常，单地址位使用一个地址字段表示一个参数，而多地址位需由两个连续地址字段共同表示。由于数据库仅支持单个地址字段存储，因此将多地址位中的首个地址字段存入数据库，后续地址字段则由应用程序通过Map结构进行维护。
     */
    public static void putMultiRegister(DeviceThingModel thingModel, DeviceLog deviceLog) {
        if (deviceLog.getVal().contains("-")) {
            BigDecimal bigDecimal = new BigDecimal(deviceLog.getVal()).add(new BigDecimal( 65536));
            deviceLog.setVal(bigDecimal.toPlainString());
        }
        //判断之前是否有首个或末尾地址是否保存
        if (MULTI_REGISTER_MAP.containsKey(thingModel.getIdentifier().replaceAll(MultiaddressEnum.MULTIADDRESS.getName(), ""))
                || MULTI_REGISTER_MAP.containsKey(thingModel.getIdentifier() + MultiaddressEnum.MULTIADDRESS.getName())) {
            //获取当前传入的 devicelog 的数据
            BigDecimal bigDecimal = new BigDecimal(deviceLog.getVal());
            String value = "";
            DeviceLog deviceLog1 = null;
            //获取 map 之前保存数据
            if (MULTI_REGISTER_MAP.containsKey(thingModel.getIdentifier() + MultiaddressEnum.MULTIADDRESS.getName())) {
                deviceLog1 = MULTI_REGISTER_MAP.get(thingModel.getIdentifier() + MultiaddressEnum.MULTIADDRESS.getName());
                //将之前的首个地址位数据与现在末尾的地址位数据进行组合
                value = Integer.toHexString(bigDecimal.intValue()) + deviceLog1.getVal();
                MULTI_REGISTER_MAP.remove(thingModel.getIdentifier() + MultiaddressEnum.MULTIADDRESS.getName());
            } else {
                deviceLog1 = MULTI_REGISTER_MAP.get(thingModel.getIdentifier().replaceAll(MultiaddressEnum.MULTIADDRESS.getName(), ""));
                //将之前的末尾地址位数据与现在首个的地址位数据进行组合
                value = deviceLog1.getVal() + Integer.toHexString(bigDecimal.intValue());
                MULTI_REGISTER_MAP.remove(thingModel.getIdentifier().replaceAll(MultiaddressEnum.MULTIADDRESS.getName(), ""));
            }
            //获取数据保存时间
            Date dataTime = deviceLog1.getDataTime();
            //获取系统当前时间
            Date date = new Date();
            //根据系统进行对比5秒,判断是否在范围内,如果不在范围内则认为是断开链接的数据
            long maxTime = date.getTime() + plcValidateTime;
            long minTime = date.getTime() - plcValidateTime;
            if (maxTime > dataTime.getTime() && minTime < dataTime.getTime()) {
                deviceLog.setVal(String.valueOf(Integer.parseInt(value, 16)));
                //是否是绝缘轴检测当前最大力矩，1#压机工作最大力矩，2#压机工作最大力矩，1#压机工作设定最大力矩，2#压机工作设定最大力矩
                if (judgmentIdentifier(thingModel.getIdentifier().replaceAll(MultiaddressEnum.MULTIADDRESS.getName(),""))) {
                    deviceLog.setVal(new BigDecimal(deviceLog.getVal()).divide(new BigDecimal(1000),2,BigDecimal.ROUND_HALF_UP).toPlainString());
                } else if (thingModel.getIdentifier().equals(DeviceParamsEnum.ENERGY_CONSUMPTION.getName())) {
                    //是否是能耗值
                    deviceLog.setVal(new BigDecimal(deviceLog.getVal()).divide(new BigDecimal(100),2,BigDecimal.ROUND_HALF_UP).divide(new BigDecimal(100),2,BigDecimal.ROUND_HALF_UP).toPlainString());
                }
                SCADA_DATA_MAP.put(thingModel.getIdentifier().replaceAll(MultiaddressEnum.MULTIADDRESS.getName(), ""), deviceLog);
                //判断是否是二维码如果是调用接口
                if (thingModel.getIdentifier().equals(DeviceParamsEnum.BAR_CODE.getName())) {
                    // 重复调用 有报错日志，先注掉
//                    baseDeliveryInfoService.updateStockQtyForScanCode(String.valueOf(new BigInteger(value, 16)));
                }
            } else {
                //由于之前数据超时将这个首位或末尾地址位数据转成 16进制保存到静态 map 中
                deviceLog.setVal(Integer.toHexString(new BigDecimal(deviceLog.getVal()).intValue()));
                MULTI_REGISTER_MAP.put(thingModel.getIdentifier(), deviceLog);
            }
        } else {
            //由于之前数据超时将这个首位或末尾地址位数据转成 16进制保存到静态 map 中
            BigDecimal bigDecimal = new BigDecimal(deviceLog.getVal());
            deviceLog.setVal(Integer.toHexString(bigDecimal.intValue()));
            MULTI_REGISTER_MAP.put(thingModel.getIdentifier(), deviceLog);
        }

    }


}
